package com.yisin.ssh2;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.yisin.ssh2.jsch.Channel;
import com.yisin.ssh2.jsch.Session;
import com.yisin.ssh2.stream.CombinedStream;

public class SshShell extends SshBase {
	private CombinedStream m_sshIO = null;
	private InputStream m_inIO = null;
	private OutputStream m_outIO = null;
	private Pattern m_expectPattern;
	private boolean m_removeTerminalChars = false;
	private boolean m_redirectToConsole = false;
	private static String escapeCharsPattern = "\\[[0-9;?]*[^0-9;]";

	public SshShell(String host, String user, String password) {
		super(host, user, password);
		Init();
	}

	public SshShell(String host, String user) {
		super(host, user);
		Init();
	}

	protected void Init() {
		setExpectPattern("");
		m_removeTerminalChars = false;
	}

	protected void OnChannelReceived() throws IOException {
		super.OnChannelReceived();
		if (m_redirectToConsole) {
			m_inIO = System.in;
			m_outIO = System.out;

			SetStream(m_inIO, m_outIO);
		} else {
			m_sshIO = GetStream();
		}
	}

	protected String getChannelType() {
		return "shell";
	}

	public CombinedStream IO() {
		return m_sshIO;
	}

	public void WriteLine(String data) throws IOException {
		Write(data + "\r");
	}

	public void Write(String data) throws IOException {
		write(data.getBytes());
	}

	// / <summary>
	// / Writes a sequence of bytes to the current stream and advances the
	// current position within this stream by the number of bytes written.
	// / </summary>
	// / <param name="buffer">An array of bytes. This method copies count bytes
	// from buffer to the current stream. </param>
	public void write(byte[] buffer) throws IOException {
		write(buffer, 0, buffer.length);
	}

	// / <summary>
	// / Writes a sequence of bytes to the current stream and advances the
	// current position within this stream by the number of bytes written.
	// / </summary>
	// / <param name="buffer">An array of bytes. This method copies count bytes
	// from buffer to the current stream. </param>
	// / <param name="offset">The zero-based byte offset in buffer at which to
	// begin copying bytes to the current stream.</param>
	// / <param name="count">The number of bytes to be written to the current
	// stream. </param>
	public void write(byte[] buffer, int offset, int count) throws IOException {
		IO().write(buffer, offset, count);
		IO().flush();
	}

	// / <summary>
	// / Creates a new I/O stream of communication with this SSH shell
	// connection
	// / </summary>
	public CombinedStream GetStream() throws IOException {
		return new CombinedStream(m_channel.getInputStream(), m_channel.getOutputStream());
	}

	public InputStream GetInputStream() throws IOException {
		return m_inIO == null ? m_channel.getInputStream() : m_inIO;
	}

	public OutputStream GetOutputStream() throws IOException {
		return m_outIO == null ? m_channel.getOutputStream() : m_outIO;
	}

	public Session getSession() {
		return m_session;
	}

	public Channel getChannel() {
		return m_channel;
	}

	public void SetStream(InputStream inputStream, OutputStream outputStream) {
		m_channel.setInputStream(inputStream);
		m_channel.setOutputStream(outputStream);
	}

	public void RedirectToConsole() {
		m_redirectToConsole = true;
	}

	public boolean isShellOpened() {
		if (m_channel != null) {
			return !m_channel.isClosed();
		}
		return false;
	}

	public boolean isShellConnected() {
		if (m_channel != null) {
			return m_channel.isConnected();
		}
		return false;
	}

	// / <summary>
	// / Gets or sets a value indicating wether this Steam sould remove the
	// terminal emulation's escape sequence characters from the response String.
	// / </summary>
	public void setRemoveTerminalEmulationCharacters(boolean value) {
		m_removeTerminalChars = value;
	}

	public boolean isRemoveTerminalEmulationCharacters() {
		return m_removeTerminalChars;
	}

	// / <summary>
	// / A regular expression pattern String to match when reading the resonse
	// using the ReadResponse() method. The default prompt value is "\n" which
	// makes the ReadRespons() method return one line of response.
	// / </summary>
	public void setExpectPattern(String value) {
		m_expectPattern = Pattern.compile(value);
	}

	public String getExpectPattern() {
		return m_expectPattern.toString();
	}

	// / <summary>
	// / Reads a response String from the SSH channel. This method will block
	// until the pattern in the 'Prompt' property is matched in the response.
	// / </summary>
	// / <returns>A response String from the SSH server.</returns>
	public String Expect() throws IOException {
		return Expect(m_expectPattern);
	}

	// / <summary>
	// / Reads a response String from the SSH channel. This method will block
	// until the pattern in the 'Prompt' property is matched in the response.
	// / </summary>
	// / <returns>A response String from the SSH server.</returns>
	public String Expect(String pattern) throws IOException {
		return Expect(Pattern.compile(pattern));
	}

	// / <summary>
	// / Reads a response String from the SSH channel. This method will block
	// until the pattern in the 'Prompt' property is matched in the response.
	// / </summary>
	// / <returns>A response String from the SSH server.</returns>
	public String Expect(Pattern pattern) throws IOException {
		int readCount;
		StringBuilder resp = new StringBuilder();
		byte[] buff = new byte[1024];
		Matcher match;
		do {
			readCount = IO().read(buff, 0, buff.length);
			if (readCount == -1)
				break;
			String tmp = new String(buff, 0, readCount);
			resp.append(tmp, 0, readCount);
			String s = resp.toString();
			match = pattern.matcher(s);
		} while (!match.find());

		String result = resp.toString();
		if (isRemoveTerminalEmulationCharacters())
			result = HandleTerminalChars(result);
		return result;
	}

	// / <summary>
	// / Removes escape sequence characters from the input string.
	// / </summary>
	public static String HandleTerminalChars(String str) {
		str = str.replace("(B)0", "");
		str = str.replace(escapeCharsPattern, "");
		str = str.replace(String.valueOf((char) 15), "");
		str = str.replace(((char) 27) + "=*", "");
		// str = Regex.Replace(str, "\\s*\r\n", "\r\n");
		return str;
	}
}
